﻿using System;
using System.Collections.Generic;
using System.Text;

namespace NETXPF.Library
{
    /// <summary>
    /// Reimplementation of "Func" from .NET 3.5.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public delegate T Function<T>();
    /// <summary>
    /// Reimplementation of "Func" from .NET 3.5.
    /// </summary>
    /// <typeparam name="Tparam1"></typeparam>
    /// <typeparam name="Tresult"></typeparam>
    /// <returns></returns>
    public delegate Tresult Function<Tparam1, Tresult>(Tparam1 a); //note: bug found on 9/7/2011 - Tparam was not included as actual parameter
    /// <summary>
    /// Reimplementation of "Func" from .NET 3.5.
    /// </summary>
    /// <typeparam name="Tparam1"></typeparam>
    /// <typeparam name="Tparam2"></typeparam>
    /// <typeparam name="Tresult"></typeparam>
    /// <returns></returns>
    public delegate Tresult Function<Tparam1, Tparam2, Tresult>(Tparam1 a, Tparam2 b);
    /// <summary>
    /// Reimplementation of "Func" from .NET 3.5.
    /// </summary>
    /// <typeparam name="Tparam1"></typeparam>
    /// <typeparam name="Tparam2"></typeparam>
    /// <typeparam name="Tparam3"></typeparam>
    /// <typeparam name="Tresult"></typeparam>
    /// <returns></returns>
    public delegate Tresult Function<Tparam1, Tparam2, Tparam3, Tresult>(Tparam1 a, Tparam2 b, Tparam3 c);

    /// <summary>
    /// Utility functions for simplifying your code.
    /// These are language-level extensions (i.e. so generic that they do not depend on any library and can be used in any context).
    /// </summary>
    public class CodeUtils
    {
        /// <summary>
        /// Executes provided function and returns its result.
        /// If there is an exception, returns the default value given.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="valueAction"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static T FailSafeNoException<T>(Function<T> valueAction, T defaultValue)
        {
            try
            {
                return valueAction.Invoke();
            }
            catch (Exception)
            {
                return defaultValue;
            }
        }

        /// <summary>
        /// Executes provided function and returns its result.
        /// If there is an exception, returns the default value given.
        /// If the returned value of the function is null, returns the default value given.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="valueAction"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static T FailSafeNotNull<T>(Function<T> valueAction, T defaultValue)
        {
            T value = FailSafeNoException(valueAction, defaultValue);
            return value != null ? value : defaultValue;
        }

        /// <summary>
        /// Runs a mapping function on the specified list, converting all values in the list to new values as specified by the function.
        /// Returns the new list. The input list is never touched.
        /// </summary>
        /// <typeparam name="T">Type of input array items</typeparam>
        /// <typeparam name="U">Type of output array items</typeparam>
        /// <param name="list"></param>
        /// <param name="mapFunction"></param>
        /// <returns></returns>
        public static List<U> Map<T, U>(IEnumerable<T> list, Function<T, U> mapFunction)
        {
            List<U> output = new List<U>();
            foreach (T obj in list)
            {
                output.Add(mapFunction(obj));
            }
            return output;
        }

        /// <summary>
        /// Executes mapping on a single object, converting it from one type (T) to another (U)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="U"></typeparam>
        /// <param name="obj"></param>
        /// <param name="mapFunction"></param>
        /// <returns></returns>
        public static U Convert<T, U>(T obj, Function<T, U> mapFunction)
        {
            return mapFunction(obj);
        }

        /// <summary>
        /// Another functional programming gem;
        /// Executes predicate, then if predicate is true calls ifTrue, else calls ifFalse;
        /// Facilitates inline conditionals in the same way as x ? y : z except with return and input type flexibility
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="U"></typeparam>
        /// <param name="condition"></param>
        /// <param name="obj"></param>
        /// <param name="ifTrue"></param>
        /// <param name="ifFalse"></param>
        /// <returns></returns>
        public static U ConditionalMap<T, U>(T obj, Predicate<T> condition, Function<T, U> ifTrue, Function<T, U> ifFalse)
        {
            if (condition(obj))
            {
                return ifTrue(obj);
            }
            return ifFalse(obj);
        }
    }
}
